package com.fimmlps.reronge.config;

import com.alibaba.fastjson2.JSON;
import com.fimmlps.reronge.filter.JwtAuthenticationFilter;
import com.fimmlps.reronge.utils.JwtUtils;
import com.fimmlps.reronge.vo.TResponseVo;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.io.IOException;
import java.io.PrintWriter;

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
        return http
                .authorizeHttpRequests(conf -> {

                    //设置允许通过过滤器获取部分无需登录验证的资源

                    conf.requestMatchers(HttpMethod.POST,"/entrySingle/getRecentHottestContentsByPage").permitAll();
                    conf.requestMatchers(HttpMethod.POST,"/entrySingle/getNewestContentsByPage").permitAll();
                    conf.requestMatchers(HttpMethod.GET,"/entrySingle/getRandomContents").permitAll();
                    conf.requestMatchers(HttpMethod.POST,"/entrySingle/getHottestContentsByPage").permitAll();
                    conf.requestMatchers(HttpMethod.POST,"/entrySingle/searchNewestContentsByPage").permitAll();
                    conf.requestMatchers(HttpMethod.POST,"/entrySingle/searchHottestContentsByPage").permitAll();
                    conf.requestMatchers(HttpMethod.POST,"/entrySingle/getEntrySingleById").permitAll();
                    conf.requestMatchers(HttpMethod.POST,"/entrySingle/getEntrySinglesByTags").permitAll();
                    conf.requestMatchers(HttpMethod.POST,"/entrySingle/getEntryTips").permitAll();

                    conf.requestMatchers(HttpMethod.POST,"/actorSingle/getRecentHottestActorsByPage").permitAll();
                    conf.requestMatchers(HttpMethod.POST,"/actorSingle/getNewestActorsByPage").permitAll();
                    conf.requestMatchers(HttpMethod.POST,"/actorSingle/getHottestActorsByPage").permitAll();
                    conf.requestMatchers(HttpMethod.POST,"/actorSingle/getActorSingleById").permitAll();
                    //tmp

                    //允许注册
                    conf.requestMatchers(HttpMethod.POST,"/entryUser/register").permitAll();
                    conf.requestMatchers(HttpMethod.GET,"/entryUser/getRerongeData").permitAll();
                    //todo
                    conf.anyRequest().authenticated();
                })
                .formLogin(conf -> {
                    conf.usernameParameter("username");
                    conf.passwordParameter("password");
                    conf.loginProcessingUrl("/login");
                    //登录成功/失败都使用handleProcess处理
                    conf.successHandler(this::handleProcess);
                    conf.failureHandler(this::handleProcess);
                    conf.permitAll();
                })
                .csrf(AbstractHttpConfigurer::disable)
                .sessionManagement(conf ->{
                    conf.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
                })
                .exceptionHandling(conf -> {
                    //授权相关异常处理
                    conf.accessDeniedHandler(this::handleProcess);
                    //验证相关异常处理
                    conf.authenticationEntryPoint(this::handleProcess);
                })
                .addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .build();
    }

    //成功/失败 通用处理器
    private void handleProcess(HttpServletRequest request, HttpServletResponse response,Object exceptionOrAuthentication) throws IOException{
        response.setContentType("application/json;charset=utf-8");
        PrintWriter writer = response.getWriter();
        if(exceptionOrAuthentication instanceof AccessDeniedException exception){
            writer.write(JSON.toJSONString(TResponseVo.error(403,exception.getMessage())));
        }
        else if(exceptionOrAuthentication instanceof AuthenticationException exception){
            writer.write(JSON.toJSONString(TResponseVo.error(401,exception.getMessage())));
        }
        else if(exceptionOrAuthentication instanceof Authentication authentication){

            //返回JWT令牌，以令客户端下次访问可携带该令牌
            writer.write(JSON.toJSONString(TResponseVo.success(JwtUtils.createJwt((User)authentication.getPrincipal()))));
        }
    }

    private void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,Object exceptionOrAuthentication) throws IOException{
        response.setContentType("application/json;charset=utf-8");
        PrintWriter writer = response.getWriter();
        String authorization = request.getHeader("Authorization");
        if(authorization != null && authorization.startsWith("Bearer ")) {
            String token = authorization.substring(7);
            //将Token加入黑名单
            if(JwtUtils.invalidate(token)) {
                //只有成功加入黑名单才会退出成功
                writer.write(JSON.toJSONString(TResponseVo.success("退出登录成功")));
                return;
            }
        }
        writer.write(JSON.toJSONString(TResponseVo.error(400, "退出登录失败")));
    }
}
